package com.pig4cloud.pig.flow.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pig.admin.api.dto.UserDTO;
import com.pig4cloud.pig.admin.api.entity.MetaEntityEntity;
import com.pig4cloud.pig.admin.api.feign.RemoteMetaEntityService;
import com.pig4cloud.pig.admin.api.feign.RemoteUserService;
import com.pig4cloud.pig.admin.api.vo.UserVO;
import com.pig4cloud.pig.common.core.constant.BpmnConstants;
import com.pig4cloud.pig.common.core.util.R;
import com.pig4cloud.pig.common.security.service.PigUser;
import com.pig4cloud.pig.common.security.util.SecurityUtils;
import com.pig4cloud.pig.flow.api.dto.ApprovalDTO;
import com.pig4cloud.pig.flow.api.dto.FlowSearchDTO;
import com.pig4cloud.pig.flow.api.dto.StartFlowDTO;
import com.pig4cloud.pig.flow.api.dto.SuspensionDTO;
import com.pig4cloud.pig.flow.api.entity.ActFlowExtendedEntity;
import com.pig4cloud.pig.flow.api.entity.ActFlowKeyExtendedEntity;
import com.pig4cloud.pig.flow.api.vo.*;
import com.pig4cloud.pig.flow.pojo.dto.FlowDTO;
import com.pig4cloud.pig.flow.pojo.vo.VersionVO;
import com.pig4cloud.pig.flow.service.ActFlowExtendedService;
import com.pig4cloud.pig.flow.service.ActFlowKeyExtendedService;
import com.pig4cloud.pig.flow.service.FlowService;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.camunda.bpm.engine.*;
import org.camunda.bpm.engine.exception.NullValueException;
import org.camunda.bpm.engine.form.StartFormData;
import org.camunda.bpm.engine.form.TaskFormData;
import org.camunda.bpm.engine.history.*;
import org.camunda.bpm.engine.repository.Deployment;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.repository.ProcessDefinitionQuery;
import org.camunda.bpm.engine.repository.ResourceDefinition;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.task.Comment;
import org.camunda.bpm.engine.task.Task;
import org.camunda.bpm.engine.task.TaskQuery;
import org.camunda.bpm.model.bpmn.BpmnModelInstance;
import org.camunda.bpm.model.bpmn.instance.ExtensionElements;
import org.camunda.bpm.model.bpmn.instance.Process;
import org.camunda.bpm.model.bpmn.instance.camunda.CamundaProperties;
import org.camunda.bpm.model.bpmn.instance.camunda.CamundaProperty;
import org.camunda.bpm.model.xml.instance.ModelElementInstance;
import org.jetbrains.annotations.NotNull;
import org.mobai.flow.enums.BpmnBaseVarEnum;
import org.mobai.flow.utils.BpmnUtil;
import org.mobai.flow.utils.FlowUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

/**
 * @author mobai
 */
@Service
@AllArgsConstructor
@Slf4j
public class FlowServiceImpl implements FlowService {
	private static final String CLASS_NAME = "[Flow Service Impl] ";


	private final RemoteMetaEntityService remoteMetaEntityService;
	private final RemoteUserService remoteUserService;

	private RepositoryService repositoryService;
	private RuntimeService runtimeService;
	private FormService formService;
	private HistoryService historyService;
	private TaskService taskService;
	private final ActFlowExtendedService actFlowExtendedService;
	private final ActFlowKeyExtendedService actFlowKeyExtendedService;
	private static ZoneId ZONE = ZoneId.systemDefault();

	@Override
	@Transactional(rollbackFor = Exception.class)
	public R<Boolean> deployment(FlowDTO flowDTO) {
		R<List<MetaEntityEntity>> details = remoteMetaEntityService.getDetails(new MetaEntityEntity() {{
			setId(flowDTO.getEntityId());
		}});
		List<MetaEntityEntity> metaEntityEntityList = details.getData();
		if (ObjectUtil.isEmpty(metaEntityEntityList)) {
			log.error("实体不存在，实体ID：{}", flowDTO.getEntityId());
			throw new RuntimeException("实体不存在");
		}
		MetaEntityEntity entity = metaEntityEntityList.stream().findFirst().orElseThrow(RuntimeException::new);

		String flowJson = JSONUtil.toJsonStr(flowDTO.getProcess());
		BpmnModelInstance bpmnModelInstance = BpmnUtil.convertToBpmn(flowJson);
		Collection<Process> childElementsByType = bpmnModelInstance.getDefinitions().getChildElementsByType(Process.class);
		Process process = childElementsByType.stream().findFirst().get();
		Optional.ofNullable(entity.getFlowKey()).ifPresent(process::setId);
		process.setName(entity.getName() + "流程");
		Deployment deploy = repositoryService.createDeployment().addModelInstance(entity.getName() + ".bpmn", bpmnModelInstance).name(entity.getName() + "流程").deploy();
		log.info("部署流程成功，流程部署ID：{}", deploy.getId());
		ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult();
		if (ObjectUtil.isNull(processDefinition)) {
			log.error("流程部署失败，流程部署ID：{}", deploy.getId());
			return R.failed("流程部署失败");
		}

		metaEntityEntityList.stream().findFirst().ifPresent(metaEntityEntity -> {
			metaEntityEntity.setFlowKey(processDefinition.getKey());
			remoteMetaEntityService.updateById(metaEntityEntity);
		});
		// 流程扩展
		actFlowExtendedService.save(new ActFlowExtendedEntity() {{
			setProcessDefinitionId(processDefinition.getId());
			setFlowJson(flowJson);
		}});
		// 流程使用版本
		actFlowKeyExtendedService.saveOrUpdate(new ActFlowKeyExtendedEntity() {{
			setFlowKey(processDefinition.getKey());
			setUseVersion(processDefinition.getVersion());
		}});
		return R.ok(true);
	}


	/**
	 * 返回不是为空的流程实例对象
	 *
	 * @param key     流程key
	 * @param version
	 * @return 流程实例对象
	 */
	@Override
	public ProcessDefinition getProcessDefinition(String key, Integer version) throws NullValueException {
		if (ObjectUtil.isNull(version)) {
			ActFlowKeyExtendedEntity actFlowKeyExtendedEntity = actFlowKeyExtendedService.getById(key);
			if (actFlowKeyExtendedEntity != null) {
				version = actFlowKeyExtendedEntity.getUseVersion();
			}
		}
		ProcessDefinitionQuery processDefinitionQuery = repositoryService.
				// 创建流程查询
						createProcessDefinitionQuery().
				// 根据流程key获取
						processDefinitionKey(key);
		// 获取流程对应的最新版本
		if (ObjectUtil.isNull(version)) {
			processDefinitionQuery.latestVersion().singleResult();
		} else {
			processDefinitionQuery.processDefinitionVersion(version);
		}


		return processDefinitionQuery.singleResult();
	}

	@Override
	public R getFlowJson(String flowKey, Integer version) {
		ProcessDefinition processDefinition = getProcessDefinition(flowKey, version);
		if (ObjectUtil.isNotNull(processDefinition)) {
			ActFlowExtendedEntity actFlowExtended = actFlowExtendedService.getById(processDefinition.getId());
			if (ObjectUtil.isNull(actFlowExtended)) {
				return R.ok();
			}
			return R.ok(JSONUtil.toBean(actFlowExtended.getFlowJson(), Map.class));
		}
		return R.ok();
	}


	@Override
	public R getFlowVersion(String key) {
		List<ProcessDefinition> processDefinitionList = repositoryService.createProcessDefinitionQuery().processDefinitionKey(key).list();
		if (ObjectUtil.isEmpty(processDefinitionList)) {
			return R.ok();
		}
		ActFlowKeyExtendedEntity actFlowKeyExtended = actFlowKeyExtendedService.getById(key);
		VersionVO versionVO = new VersionVO();
		versionVO.setUseVersion(actFlowKeyExtended.getUseVersion());
		versionVO.setVersionList(processDefinitionList.stream().map(ResourceDefinition::getVersion).sorted(Comparator.reverseOrder()).toList());
		return R.ok(versionVO);
	}


	@Override
	public R getFlowJsonByProcessInstanceId(String processInstanceId) {
		HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
		return R.ok(actFlowExtendedService.getById(historicProcessInstance.getProcessDefinitionId()));
	}

	@Override
	public R updateFlowVersion(String key, Integer version) {
		ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(key).processDefinitionVersion(version).singleResult();
		if (ObjectUtil.isNull(processDefinition)) {
			return R.failed("流程版本不存在");
		}
		return R.ok(actFlowKeyExtendedService.updateById(new ActFlowKeyExtendedEntity() {{
			setFlowKey(key);
			setUseVersion(version);
		}}));
	}


	@Override
	@Transactional(rollbackFor = Exception.class)
	public R<String> startProcessInstance(StartFlowDTO startFlowDTO) {
		ProcessDefinition processDefinition = getProcessDefinition(startFlowDTO.getFlowKey(), null);
		if (ObjectUtil.isNull(processDefinition)) {
			return R.failed("流程不存在");
		}
		StartFormData startFormData = formService.getStartFormData(processDefinition.getId());
		try {
			FlowUtil.validateFormField(startFlowDTO.getParams(), startFormData.getFormFields());
		} catch (Exception e) {
			return R.failed("流程发起时，" + e.getMessage());
		}
		ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId(), startFlowDTO.getParams());
		log.info("流程启动成功，流程实例ID：{}", processInstance.getId());
		return R.ok(processInstance.getId());
	}


	@SneakyThrows
	@Override
	public R getHistoryNode(String processInstanceId) {
		// 获取历史节点
		CompletableFuture<List<HistoricActivityInstance>> completableFutureHistoricActivityInstanceList = CompletableFuture.supplyAsync(() -> historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list());
		// 获取历史任务
		CompletableFuture<List<HistoricTaskInstance>> completableFutureHistoricTaskInstanceList = CompletableFuture.supplyAsync(() -> historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId).list());
		// 获取评论
		CompletableFuture<List<Comment>> completableFutureProcessInstanceCommentList = CompletableFuture.supplyAsync(() -> taskService.getProcessInstanceComments(processInstanceId));
		// 获取流程实例
		CompletableFuture<HistoricProcessInstance> historicProcessInstanceCompletableFuture = CompletableFuture.supplyAsync(() -> historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult());
		// 等待所有任务完成
		CompletableFuture.allOf(completableFutureHistoricActivityInstanceList, completableFutureHistoricTaskInstanceList, completableFutureProcessInstanceCommentList, historicProcessInstanceCompletableFuture);

		List<HistoricActivityInstance> historicActivityInstanceList = completableFutureHistoricActivityInstanceList.get();
		List<HistoricTaskInstance> historicTaskInstanceList = completableFutureHistoricTaskInstanceList.get();
		List<Comment> processInstanceCommentList = completableFutureProcessInstanceCommentList.get();
		// 获取当前登录用户
		PigUser loginUser = SecurityUtils.getUser();
		// 获取任务列表里的所有用户
		CompletableFuture<Map<String, List<HistoricTaskInstance>>> mapCompletableFutureHistoricTaskInstanceMap = CompletableFuture.supplyAsync(() -> historicTaskInstanceList.stream().collect(Collectors.groupingBy(HistoricTaskInstance::getActivityInstanceId)));
		// 获取用户对应关系
		CompletableFuture<Map<String, UserVO>> completableFutureUserMap = CompletableFuture.supplyAsync(() -> {
			List<String> userIdList = historicTaskInstanceList.stream().map(HistoricTaskInstance::getAssignee).distinct().collect(Collectors.toList());
			Optional.ofNullable(loginUser).ifPresent(user -> userIdList.add(user.getId().toString()));
			R<List<UserVO>> userList = remoteUserService.listByUserIdsList(new UserDTO() {{
				setUserIdList(userIdList);
			}});
			return userList.getData().stream().collect(Collectors.toMap(user -> user.getUserId().toString(), user -> user));
		});

		// 获取评论
		CompletableFuture<Map<String, List<Comment>>> mapCompletableFutureCommentMap = CompletableFuture.supplyAsync(() -> processInstanceCommentList.stream().collect(Collectors.groupingBy(Comment::getTaskId)));

		CompletableFuture<Map<String, List<HistoricVariableInstance>>> historicVariableMapFuture = CompletableFuture.supplyAsync(() -> {
			List<HistoricVariableInstance> historicVariableInstances = historyService.createHistoricVariableInstanceQuery().taskIdIn(historicTaskInstanceList.stream().map(HistoricTaskInstance::getId).toArray(String[]::new)).list();
			return historicVariableInstances.stream().collect(Collectors.groupingBy(HistoricVariableInstance::getTaskId));
		});
		// 等待所有任务完成
		CompletableFuture.allOf(mapCompletableFutureHistoricTaskInstanceMap, mapCompletableFutureCommentMap, completableFutureUserMap, historicVariableMapFuture);


		Map<String, List<HistoricTaskInstance>> historicTaskInstanceMap = mapCompletableFutureHistoricTaskInstanceMap.get();
		Map<String, List<Comment>> commentMap = mapCompletableFutureCommentMap.get();
		Map<String, UserVO> userMap = completableFutureUserMap.get();
		Map<String, List<HistoricVariableInstance>> historicVariableMap = historicVariableMapFuture.get();

		// 对历史节点进行倒排序
		List<ActivityInstanceVO> activityInstanceVOList = new ArrayList<>();
		Map<String, List<HistoricActivityInstance>> historicActivityInstanceMap = historicActivityInstanceList.stream().filter(historicActivityInstance -> ObjectUtil.equal(BpmnConstants.USER_TASK, historicActivityInstance.getActivityType())).sorted(Comparator.comparing(HistoricActivityInstance::getStartTime)).collect(Collectors.groupingBy(HistoricActivityInstance::getParentActivityInstanceId));
		historicActivityInstanceMap.forEach((parentActivityInstanceId, activityInstanceList) -> {
			AtomicBoolean isCompleted = new AtomicBoolean(true);
			List<TaskVO> taskList = new ArrayList<>();
			activityInstanceList.stream().sorted(Comparator.comparing(HistoricActivityInstance::getStartTime)).forEach(activityInstance -> {
				List<HistoricTaskInstance> historicTaskInstances = historicTaskInstanceMap.get(activityInstance.getId());
				Optional.ofNullable(historicTaskInstances).ifPresent(hTaskList -> {
					hTaskList.stream()
							.filter(historicTaskInstance -> ObjectUtil.notEqual(BpmnConstants.DELETED, historicTaskInstance.getDeleteReason()))
							.sorted(Comparator.comparing(HistoricTaskInstance::getStartTime))
							.forEach(historicTaskInstance -> {
								TaskVO taskVO = new TaskVO();
								taskVO.setId(historicTaskInstance.getId());
								taskVO.setName(historicTaskInstance.getName());
								taskVO.setAssignee(userMap.get(historicTaskInstance.getAssignee()));
								Optional.ofNullable(loginUser).ifPresent(user -> {
									taskVO.setAssigneeEd(ObjectUtil.equal(user.getId().toString(), historicTaskInstance.getAssignee()));
									taskVO.setCurrentEd(ObjectUtil.equal(user.getId().toString(), historicTaskInstance.getAssignee()) && ObjectUtil.isEmpty(historicTaskInstance.getEndTime()));
								});
								taskVO.setStartTime(historicTaskInstance.getStartTime());
								Optional.ofNullable(historicTaskInstance.getEndTime()).ifPresentOrElse(taskVO::setEndTime, () -> {
									isCompleted.set(false);
								});
								taskVO.setDurationInMillis(historicTaskInstance.getDurationInMillis());
								taskVO.setDeleteReason(historicTaskInstance.getDeleteReason());
								Optional.ofNullable(historicVariableMap.get(historicTaskInstance.getId())).ifPresent(varList -> {
									varList.forEach(var -> {
										taskVO.getVariables().put(var.getName(), var.getValue());
									});
								});
								Optional.ofNullable(commentMap.get(historicTaskInstance.getId())).ifPresent(comments -> taskVO.setComment(comments.stream().map(Comment::getFullMessage).collect(Collectors.joining(","))));
								taskList.add(taskVO);
							});
				});
			});
			ActivityInstanceVO activityInstanceVO = new ActivityInstanceVO();
			activityInstanceList.stream().min(Comparator.comparing(HistoricActivityInstance::getStartTime)).ifPresent(activityInstance -> {
				activityInstanceVO.setActivityInstanceId(activityInstance.getId());
				activityInstanceVO.setActivityId(activityInstance.getActivityId());
				activityInstanceVO.setName(activityInstance.getActivityName());
				activityInstanceVO.setStartTime(activityInstance.getStartTime());
				activityInstanceVO.setEndTime(activityInstance.getEndTime());
				activityInstanceVO.setTaskList(taskList);
				activityInstanceVO.setRemovalTime(activityInstance.getRemovalTime());
				activityInstanceVO.setCompleteEd(isCompleted.get());
			});
			activityInstanceVOList.add(activityInstanceVO);

		});
		HistoricProcessInstance historicProcessInstance = historicProcessInstanceCompletableFuture.get();
		return R.ok(new HistoryNodeVO() {{
			setProcessInstanceId(historicProcessInstance.getId());
			setStartTime(historicProcessInstance.getStartTime());
			setEndTime(historicProcessInstance.getEndTime());
			setState(historicProcessInstance.getState());
			setName(historicProcessInstance.getProcessDefinitionName());
			setDurationInMillis(historicProcessInstance.getDurationInMillis());
			setStartUser(userMap.get(historicProcessInstance.getStartUserId()));
			setActivityInstanceList(activityInstanceVOList.stream().sorted(Comparator.comparing(ActivityInstanceVO::getStartTime)).toList());
		}});
	}


	@Override
	public R completeTask(ApprovalDTO approvalDTO) {
		PigUser user = SecurityUtils.getUser();
		Task task = taskService.createTaskQuery().taskId(approvalDTO.getTaskId()).taskAssignee(user.getId().toString()).singleResult();
		if (ObjectUtil.isNull(task)) {
			return R.failed("任务不存在或已处理");
		}
		TaskFormData taskFormData = formService.getTaskFormData(approvalDTO.getTaskId());
		Optional.ofNullable(approvalDTO.getParams()).ifPresent(params -> {
			FlowUtil.validateFormField(approvalDTO.getParams(), taskFormData.getFormFields());
			taskService.setVariables(task.getId(), params);
		});

		taskService.createComment(approvalDTO.getTaskId(), task.getProcessInstanceId(), approvalDTO.getComment());
		taskService.setVariableLocal(task.getId(), BpmnBaseVarEnum.MOBAI_FLOW_STATUS.getType(), approvalDTO.getStatus());

		taskService.complete(task.getId(), new HashMap<>() {{
			put(BpmnBaseVarEnum.MOBAI_FLOW_STATUS.getType(), approvalDTO.getStatus());
		}});
		return R.ok();
	}

	@Override
	public R updateSuspendState(SuspensionDTO suspensionDTO) {
		if (suspensionDTO.getSuspensionStatus()) {
			runtimeService.suspendProcessInstanceById(suspensionDTO.getProcessInstanceId());
		} else {
			runtimeService.activateProcessInstanceById(suspensionDTO.getProcessInstanceId());
		}
		return R.ok();
	}

	@SneakyThrows
	@Override
	public Page<FlowVO> myStartPage(Page page, FlowSearchDTO flowSearchDTO) {
		Page<FlowVO> flowPage = new Page<>();
		PigUser user = SecurityUtils.getUser();
		HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery().orderByProcessInstanceStartTime().desc().startedBy(user.getId().toString());

		if (ObjectUtil.isNotEmpty(flowSearchDTO)) {
			Optional.ofNullable(flowSearchDTO.getApprovalState()).ifPresent(approvalState -> {
				switch (approvalState) {
					case "ACTIVE" -> historicProcessInstanceQuery.unfinished();
					case "COMPLETED" -> historicProcessInstanceQuery.finished();
				}
			});
			if (ObjectUtil.isNotEmpty(flowSearchDTO.getStartTime()) && flowSearchDTO.getStartTime().length >= 2) {
				// localDateTime转换成Date
				historicProcessInstanceQuery.startedAfter(Date.from(flowSearchDTO.getStartTime()[0].atZone(ZONE).toInstant()));
				historicProcessInstanceQuery.startedBefore(Date.from(flowSearchDTO.getStartTime()[1].atZone(ZONE).toInstant()));
			}
			if (ObjectUtil.isNotEmpty(flowSearchDTO.getEndTime()) && flowSearchDTO.getEndTime().length >= 2) {

				historicProcessInstanceQuery.finishedAfter(Date.from(flowSearchDTO.getEndTime()[0].atZone(ZONE).toInstant()));
				historicProcessInstanceQuery.finishedBefore(Date.from(flowSearchDTO.getEndTime()[1].atZone(ZONE).toInstant()));
			}


		}

		long count = historicProcessInstanceQuery.count();
		if (count == 0) {
			return getFlowPage(page, flowPage, count, List.of());
		}


		List<HistoricProcessInstance> historicProcessInstanceList = historicProcessInstanceQuery.listPage(
				// 使用的是limiter，所以需要手动设置当前页
				(int) ((page.getCurrent() - 1) * page.getSize()), (int) page.getSize());

		List<String> flowKeyList = historicProcessInstanceList.stream().map(HistoricProcessInstance::getProcessDefinitionKey).toList();
		String[] processInstanceIds = historicProcessInstanceList.stream().map(HistoricProcessInstance::getId).toArray(String[]::new);


		CompletableFuture<Map<String, MetaEntityEntity>> flowKeyMapFuture = CompletableFuture.supplyAsync(() -> {
			R<List<MetaEntityEntity>> rFlowKeyList = remoteMetaEntityService.queryListByFlowKeyList(flowKeyList);
			return rFlowKeyList.getData().stream().collect(Collectors.toMap(MetaEntityEntity::getFlowKey, s -> s));
		});


		CompletableFuture<Map<Object, List<Map<String, Object>>>> valueMapFuture = CompletableFuture.supplyAsync(() -> getValueMap(processInstanceIds));
		CompletableFuture<List<Task>> taskListFuture = CompletableFuture.supplyAsync(() -> taskService.createTaskQuery().processInstanceIdIn(processInstanceIds).list());


		CompletableFuture.allOf(flowKeyMapFuture, valueMapFuture, taskListFuture);
		Map<String, MetaEntityEntity> flowKeyMap = flowKeyMapFuture.get();
		Map<Object, List<Map<String, Object>>> valueMap = valueMapFuture.get();
		List<Task> taskList = taskListFuture.get();

		Map<String, List<Task>> taskMap = taskList.stream().collect(Collectors.groupingBy(Task::getProcessInstanceId));
		Map<String, UserVO> userMap = getUserMap(taskList, user, historicProcessInstanceList);

		List<FlowVO> flowVOList = historicProcessInstanceList.stream().map(historicProcessInstance -> {
			FlowVO flowVO = new FlowVO();
			setCommonFlowVO(historicProcessInstance, flowVO, userMap, flowKeyMap, valueMap);
			List<Task> tasks = taskMap.get(historicProcessInstance.getId());
			if (ObjectUtil.isNotEmpty(tasks)) {
				flowVO.setApprovalNodeName(tasks.stream().map(Task::getName).distinct().collect(Collectors.joining(",")));
			}
			return flowVO;
		}).toList();
		return getFlowPage(page, flowPage, count, flowVOList);
	}


	@SneakyThrows
	@Override
	public Page<FlowVO> myApprovalPage(Page page, FlowSearchDTO flowSearchDTO) {
		Page<FlowVO> flowPage = new Page<>();
		PigUser user = SecurityUtils.getUser();
		TaskQuery taskQuery = taskService.createTaskQuery().taskAssignee(user.getId().toString());
		if (ObjectUtil.isNotEmpty(flowSearchDTO)) {
			if (ObjectUtil.isNotEmpty(flowSearchDTO.getStartTime()) && flowSearchDTO.getStartTime().length >= 2) {
				taskQuery.taskCreatedAfter(Date.from(flowSearchDTO.getStartTime()[0].atZone(ZONE).toInstant()));
				taskQuery.taskCreatedBefore(Date.from(flowSearchDTO.getStartTime()[1].atZone(ZONE).toInstant()));
			}
		}


		taskQuery.orderByTaskCreateTime().desc();
		long count = taskQuery.count();
		if (count == 0) {
			return getFlowPage(page, flowPage, count, List.of());
		}
		List<Task> taskList = taskQuery.taskAssignee(user.getId().toString()).listPage(
				// 使用的是limiter，所以需要手动设置当前页
				(int) ((page.getCurrent() - 1) * page.getSize()), (int) page.getSize());


		Map<String, List<Task>> taskMap = taskList.stream().collect(Collectors.groupingBy(Task::getProcessInstanceId));


		List<HistoricProcessInstance> historicProcessInstanceList = historyService.createHistoricProcessInstanceQuery().active().processInstanceIds(taskList.stream().map(Task::getProcessInstanceId).collect(Collectors.toSet())).list();
		CompletableFuture<Map<String, HistoricProcessInstance>> processInstanceMapFuture = CompletableFuture.supplyAsync(() -> historicProcessInstanceList.stream().collect(Collectors.toMap(HistoricProcessInstance::getId, s -> s)));
		CompletableFuture<String[]> processInstanceIdsFuture = CompletableFuture.supplyAsync(() -> historicProcessInstanceList.stream().map(HistoricProcessInstance::getId).toArray(String[]::new));
		CompletableFuture<List<String>> flowKeyListFuture = CompletableFuture.supplyAsync(() -> historicProcessInstanceList.stream().map(HistoricProcessInstance::getProcessDefinitionKey).toList());

		CompletableFuture.allOf(processInstanceMapFuture, processInstanceIdsFuture, flowKeyListFuture);

		Map<String, HistoricProcessInstance> processInstanceMap = processInstanceMapFuture.get();
		String[] processInstanceIds = processInstanceIdsFuture.get();
		List<String> flowKeyList = flowKeyListFuture.get();


		CompletableFuture<Map<String, MetaEntityEntity>> flowKeyMapFuture = CompletableFuture.supplyAsync(() -> {
			R<List<MetaEntityEntity>> rFlowKeyList = remoteMetaEntityService.queryListByFlowKeyList(flowKeyList);
			return rFlowKeyList.getData().stream().collect(Collectors.toMap(MetaEntityEntity::getFlowKey, s -> s));
		});


		CompletableFuture<Map<String, UserVO>> userMapFuture = CompletableFuture.supplyAsync(() -> getUserMap(taskList, user, historicProcessInstanceList));
		CompletableFuture<Map<Object, List<Map<String, Object>>>> valueMapFuture = CompletableFuture.supplyAsync(() -> getValueMap(processInstanceIds));

		CompletableFuture.allOf(flowKeyMapFuture, userMapFuture, valueMapFuture);

		Map<String, UserVO> userMap = userMapFuture.get();
		Map<Object, List<Map<String, Object>>> valueMap = valueMapFuture.get();
		Map<String, MetaEntityEntity> flowKeyMap = flowKeyMapFuture.get();


		List<FlowVO> flowVOList = taskList.stream().map(task -> {
			FlowVO flowVO = new FlowVO();
			HistoricProcessInstance historicProcessInstance = processInstanceMap.get(task.getProcessInstanceId());
			if (ObjectUtil.isEmpty(historicProcessInstance)) {
				return null;
			}
			flowVO.setName(task.getName());
			flowVO.setTaskId(task.getId());
			setCommonFlowVO(historicProcessInstance, flowVO, userMap, flowKeyMap, valueMap);
			List<Task> tasks = taskMap.get(historicProcessInstance.getId());
			if (ObjectUtil.isNotEmpty(tasks)) {
				flowVO.setApprovalNodeName(tasks.stream().map(Task::getName).distinct().collect(Collectors.joining(",")));
			}
			flowVO.setTask(new TaskVO() {{
				setId(task.getId());
				setName(task.getName());
				setStartTime(task.getCreateTime());
			}});

			return flowVO;
		}).filter(Objects::nonNull).toList();


		return getFlowPage(page, flowPage, count, flowVOList);
	}


	@SneakyThrows
	@Override
	public Page<FlowVO> myDonePage(Page page, FlowSearchDTO flowSearchDTO) {
		Page<FlowVO> flowPage = new Page<>();
		PigUser user = SecurityUtils.getUser();

		HistoricTaskInstanceQuery historicTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery().taskAssignee(user.getId().toString());
		if (ObjectUtil.isNotEmpty(flowSearchDTO)) {
			if (ObjectUtil.isNotEmpty(flowSearchDTO.getStartTime()) && flowSearchDTO.getStartTime().length >= 2) {
				historicTaskInstanceQuery.startedAfter(Date.from(flowSearchDTO.getStartTime()[0].atZone(ZONE).toInstant()));
				historicTaskInstanceQuery.startedBefore(Date.from(flowSearchDTO.getStartTime()[1].atZone(ZONE).toInstant()));
			}
			if (ObjectUtil.isNotEmpty(flowSearchDTO.getEndTime()) && flowSearchDTO.getEndTime().length >= 2) {
				historicTaskInstanceQuery.finishedAfter(Date.from(flowSearchDTO.getEndTime()[0].atZone(ZONE).toInstant()));
				historicTaskInstanceQuery.finishedBefore(Date.from(flowSearchDTO.getEndTime()[1].atZone(ZONE).toInstant()));
			}
		}
		historicTaskInstanceQuery.finished().orderByHistoricTaskInstanceEndTime().desc();
		long count = historicTaskInstanceQuery.count();
		if (count == 0) {
			return getFlowPage(page, flowPage, count, List.of());
		}
		List<HistoricTaskInstance> historicTaskList = historicTaskInstanceQuery.taskAssignee(user.getId().toString()).listPage(
				// 使用的是limiter，所以需要手动设置当前页
				(int) ((page.getCurrent() - 1) * page.getSize()), (int) page.getSize());
		List<HistoricProcessInstance> historicProcessInstanceList = historyService.createHistoricProcessInstanceQuery().processInstanceIds(historicTaskList.stream().map(HistoricTaskInstance::getProcessInstanceId).collect(Collectors.toSet())).list();
		CompletableFuture<Map<String, HistoricProcessInstance>> processInstanceMapFuture = CompletableFuture.supplyAsync(() -> historicProcessInstanceList.stream().collect(Collectors.toMap(HistoricProcessInstance::getId, s -> s)));
		CompletableFuture<String[]> processInstanceIdsFuture = CompletableFuture.supplyAsync(() -> historicProcessInstanceList.stream().map(HistoricProcessInstance::getId).toArray(String[]::new));
		CompletableFuture<List<String>> flowKeyListFuture = CompletableFuture.supplyAsync(() -> historicProcessInstanceList.stream().map(HistoricProcessInstance::getProcessDefinitionKey).toList());

		CompletableFuture<List<Task>> taskCompletableFuture = CompletableFuture.supplyAsync(() -> taskService.createTaskQuery().processInstanceIdIn(historicTaskList.stream().map(HistoricTaskInstance::getProcessInstanceId).toArray(String[]::new)).list());

		CompletableFuture.allOf(processInstanceMapFuture, processInstanceIdsFuture, flowKeyListFuture);
		Map<String, HistoricProcessInstance> processInstanceMap = processInstanceMapFuture.get();
		String[] processInstanceIds = processInstanceIdsFuture.get();
		List<String> flowKeyList = flowKeyListFuture.get();
		List<Task> taskList = taskCompletableFuture.get();
		CompletableFuture<Map<Object, List<Map<String, Object>>>> valueMapFuture = CompletableFuture.supplyAsync(() -> getValueMap(processInstanceIds));
		CompletableFuture<Map<String, MetaEntityEntity>> flowKeyMapFuture = CompletableFuture.supplyAsync(() -> {
			R<List<MetaEntityEntity>> rFlowKeyList = remoteMetaEntityService.queryListByFlowKeyList(flowKeyList);
			return rFlowKeyList.getData().stream().collect(Collectors.toMap(MetaEntityEntity::getFlowKey, s -> s));
		});
		CompletableFuture<Map<String, UserVO>> userMapFuture = CompletableFuture.supplyAsync(() -> getUserMapByHistoric(historicTaskList, user, historicProcessInstanceList));
		CompletableFuture<Map<String, List<Task>>> taskMapCompletableFuture = CompletableFuture.supplyAsync(() -> taskList.stream().collect(Collectors.groupingBy(Task::getProcessInstanceId)));
		CompletableFuture.allOf(flowKeyMapFuture, userMapFuture, valueMapFuture, taskMapCompletableFuture);
		Map<Object, List<Map<String, Object>>> valueMap = valueMapFuture.get();
		Map<String, MetaEntityEntity> flowKeyMap = flowKeyMapFuture.get();
		Map<String, UserVO> userMap = userMapFuture.get();
		Map<String, List<Task>> taskMap = taskMapCompletableFuture.get();
		List<FlowVO> flowVOList = historicTaskList.stream().map(task -> {
			FlowVO flowVO = new FlowVO();
			HistoricProcessInstance historicProcessInstance = processInstanceMap.get(task.getProcessInstanceId());
			if (ObjectUtil.isEmpty(historicProcessInstance)) {
				return null;
			}
			flowVO.setName(task.getName());
			flowVO.setTaskId(task.getId());
			setCommonFlowVO(historicProcessInstance, flowVO, userMap, flowKeyMap, valueMap);
			Optional.ofNullable(taskMap.get(task.getProcessInstanceId())).ifPresent(tasks -> flowVO.setApprovalNodeName(tasks.stream().map(Task::getName).distinct().collect(Collectors.joining(","))));
			flowVO.setTask(new TaskVO() {{
				setId(task.getId());
				setName(task.getName());
				setStartTime(task.getStartTime());
				setEndTime(task.getStartTime());
				setDurationInMillis(task.getDurationInMillis());
				setAssignee(userMap.get(task.getAssignee()));
			}});
			return flowVO;
		}).filter(Objects::nonNull).toList();
		return getFlowPage(page, flowPage, count, flowVOList);
	}

	private Map<String, UserVO> getUserMapByHistoric(List<HistoricTaskInstance> taskList, PigUser user, List<HistoricProcessInstance> historicProcessInstanceList) {
		List<String> userIdList = taskList.stream().map(HistoricTaskInstance::getAssignee).distinct().collect(Collectors.toList());
		userIdList.addAll(historicProcessInstanceList.stream().map(HistoricProcessInstance::getStartUserId).distinct().toList());
		userIdList.add(user.getId().toString());
		R<List<UserVO>> rUserList = remoteUserService.listByUserIdsList(new UserDTO() {{
			setUserIdList(userIdList);
		}});
		Map<String, UserVO> userMap = rUserList.getData().stream().collect(Collectors.toMap(s -> s.getUserId().toString(), s -> s));
		return userMap;
	}

	private static void setCommonFlowVO(HistoricProcessInstance historicProcessInstance, FlowVO flowVO, Map<String, UserVO> userMap, Map<String, MetaEntityEntity> flowKeyMap, Map<Object, List<Map<String, Object>>> valueMap) {
		flowVO.setEndTime(historicProcessInstance.getEndTime());
		flowVO.setProcessInstanceId(historicProcessInstance.getId());
		flowVO.setTitle(historicProcessInstance.getProcessDefinitionName());
		flowVO.setStartUser(userMap.get(historicProcessInstance.getStartUserId()));
		flowVO.setStartTime(historicProcessInstance.getStartTime());
		flowVO.setState(historicProcessInstance.getState());
		flowVO.setEntity(flowKeyMap.get(historicProcessInstance.getProcessDefinitionKey()));
		flowVO.setFlowKey(historicProcessInstance.getProcessDefinitionKey());
		Optional.ofNullable(historicProcessInstance.getDurationInMillis()).ifPresentOrElse(
				d -> flowVO.setDurationInMillis(historicProcessInstance.getDurationInMillis()),
				() -> flowVO.setDurationInMillis(System.currentTimeMillis() - flowVO.getStartTime().getTime())
		);
		Optional.ofNullable(valueMap.get(historicProcessInstance.getId())).ifPresent(variableList -> {
			variableList.forEach(map -> {
				flowVO.getVariables().put(map.get("name").toString(), map.get("value"));
			});
		});
	}

	@NotNull
	private Page<FlowVO> getFlowPage(Page page, Page<FlowVO> flowPage, long count, List<FlowVO> flowVOList) {
		flowPage.setTotal(count);
		flowPage.setRecords(flowVOList);
		flowPage.setCurrent(page.getCurrent());
		flowPage.setSize(page.getSize());
		if (count > 0) {
			flowPage.setPages(count % page.getSize() == 0 ? count / page.getSize() : count / page.getSize() + 1);
		} else {
			flowPage.setPages(0);
		}
		return flowPage;
	}


	@NotNull
	private Map<Object, List<Map<String, Object>>> getValueMap(String[] processInstanceIds) {
		List<HistoricVariableInstance> historicVariableInstanceList = historyService.createHistoricVariableInstanceQuery().processInstanceIdIn(processInstanceIds).list();
		Map<Object, List<Map<String, Object>>> valueMap = historicVariableInstanceList.stream().map(s -> {
			Map<String, Object> hashMap = new HashMap<>();
			hashMap.put("processInstanceId", s.getProcessInstanceId());
			hashMap.put("name", s.getName());
			hashMap.put("value", s.getValue());

			return hashMap;
		}).collect(Collectors.groupingBy(s -> s.get("processInstanceId")));
		return valueMap;
	}

	@NotNull
	private Map<String, UserVO> getUserMap(List<Task> taskList, PigUser user, List<HistoricProcessInstance> historicProcessInstanceList) {
		List<String> userIdList = taskList.stream().map(Task::getAssignee).distinct().collect(Collectors.toList());
		userIdList.addAll(historicProcessInstanceList.stream().map(HistoricProcessInstance::getStartUserId).distinct().toList());
		userIdList.add(user.getId().toString());
		R<List<UserVO>> rUserList = remoteUserService.listByUserIdsList(new UserDTO() {{
			setUserIdList(userIdList);
		}});
		Map<String, UserVO> userMap = rUserList.getData().stream().collect(Collectors.toMap(s -> s.getUserId().toString(), s -> s));
		return userMap;
	}

	@Override
	public R getTaskPermission(String taskId) {
		Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		BpmnModelInstance bpmnModelInstance = repositoryService.getBpmnModelInstance(task.getProcessDefinitionId());
		ModelElementInstance taskElement = bpmnModelInstance.getModelElementById(task.getTaskDefinitionKey());
		Map<String, Object> extensionProperties = getExtensionProperties(taskElement);

		TaskFormData taskFormData = formService.getTaskFormData(taskId);
		List<FieldVO> list = taskFormData.getFormFields().stream().map(formField -> {
			FieldVO fieldVO = new FieldVO();
			fieldVO.setId(formField.getId());
			Optional.ofNullable(formField.getProperties()).ifPresent(properties -> {
				fieldVO.setHidden(Boolean.parseBoolean(properties.get("hidden")));
				fieldVO.setReadonly(Boolean.parseBoolean(properties.get("readonly")));
				fieldVO.setRequired(Boolean.parseBoolean(properties.get("required")));
			});
			return fieldVO;

		}).toList();
		TaskVO taskVO = new TaskVO();
		taskVO.setId(task.getId());
		taskVO.setName(task.getName());
		taskVO.setStartTime(task.getCreateTime());
		taskVO.setFieldList(list);
		taskVO.setExtensionProperties(extensionProperties);
		return R.ok(taskVO);
	}

	@NotNull
	private static Map<String, Object> getExtensionProperties(ModelElementInstance taskElement) {
		Map<String, Object> extensionProperties = new HashMap<>();
		taskElement.getChildElementsByType(ExtensionElements.class).stream().findFirst().flatMap(extensionElement -> extensionElement.getChildElementsByType(CamundaProperties.class).stream().findFirst()).ifPresent(camundaPropertiesElement -> {
			Collection<CamundaProperty> camundaProperties = camundaPropertiesElement.getCamundaProperties();
			for (CamundaProperty camundaProperty : camundaProperties) {
				extensionProperties.put(camundaProperty.getCamundaName(), camundaProperty.getCamundaValue());
			}
		});
		return extensionProperties;
	}

	@Override
	public R terminationProcess(ApprovalDTO approvalDTO) {
		Optional.ofNullable(taskService.createTaskQuery().taskId(approvalDTO.getTaskId()).singleResult()).ifPresent(task -> {
			taskService.setVariableLocal(task.getId(), BpmnBaseVarEnum.MOBAI_FLOW_STATUS.getType(), approvalDTO.getStatus());
			taskService.createComment(task.getId(), task.getProcessInstanceId(), approvalDTO.getComment());
			runtimeService.deleteProcessInstance(task.getProcessInstanceId(), approvalDTO.getComment());
		});

		return R.ok();
	}
}
